package com.rzjm.api.netty.water;

import com.rzjm.api.netty.water.handler.LinkManagementHandler;
import com.rzjm.api.netty.water.handler.MessageReceiverHandler;
import com.rzjm.api.netty.water.handler.OutBoundHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component("TCPServer")
public class TCPServer {

    private static final Logger LOGGER = LoggerFactory.getLogger(TCPServer.class);

    // 连接时间配置
    private final static int READER_IDLE_TIME_SECONDS = 90;
    private final static int WRITER_IDLE_TIME_SECONDS = 0;
    private final static int ALL_IDLE_TIME_SECONDS = 0;

    // 长度解码器参数
    private final static int MAX_FRAME_LEN = 65535;
    private final static int LENGTH_FIELD_OFFSET = 2;
    private final static int LENGTH_FIELD_LEN = 2;
    private final static int LENGTH_ADJUSTMENT = 8;
    private final static int INITIAL_BYTES_TO_TRIP = 0;

    @Value("${socketServer.port}")
    private int port;

    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;


    public void start() {
        boolean flag = NettyUtil.isLinux();
        if (flag) {
            bossGroup = new EpollEventLoopGroup(1);
            workerGroup = new EpollEventLoopGroup(200);
        } else {
            bossGroup = new NioEventLoopGroup(1);
            workerGroup = new NioEventLoopGroup(200);
        }

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup);
            serverBootstrap.channel(flag ? EpollServerSocketChannel.class : NioServerSocketChannel.class);
            serverBootstrap.childHandler(new ServerChannelInitializer());
            // 服务端接受连接的队列长度，如果队列已满，客户端连接将被拒绝。默认值，Windows为200，其他为128。
            serverBootstrap.option(ChannelOption.SO_BACKLOG, 4096);
            serverBootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
            serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true); //禁用nagle算法
            //发送缓冲区的大小
            serverBootstrap.childOption(ChannelOption.SO_SNDBUF, 1024 * 100);
            //接收缓冲区的大小
            serverBootstrap.childOption(ChannelOption.SO_RCVBUF, 1024 * 100);
            serverBootstrap.option(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(1024 * 100));
            // 两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文
            serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
            serverBootstrap.handler(new LoggingHandler(LogLevel.INFO));
            //绑定端口,开始接收进来的连接
            ChannelFuture f = serverBootstrap.bind(port).sync();

            f.addListener(new ServerStarterListener());
            // Wait until the server socket is closed.
            //f.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @PostConstruct
    public void initServer() {
        LOGGER.info("==============PostConstruct start server LISITEN = {}", this.port);
        this.start();
    }

    @PreDestroy
    public void stop() {
        LOGGER.info("==============PreDestroy start server");
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }

    /**
     * channel 初始化
     */
    static class ServerChannelInitializer extends ChannelInitializer {

        @Override
        protected void initChannel(Channel ch) throws Exception {
            //目前大棚协议为json数据，并且数据只有一层{}，例如：{xx:xxx, xx:xxx}
            //delimiter结束符使用【}】,代表报文结束符为【}】，用【}】分割成多个报文接收，没有【}】的报文将继续等待接收数据，直到完整，解决粘包半包。
            ByteBuf delimiter = Unpooled.copiedBuffer("}".getBytes());
            ch.pipeline().addLast("framer", new DelimiterBasedFrameDecoder(8192,false, delimiter));
            ch.pipeline().addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
            ch.pipeline().addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
            //outhandler 报文发送处理器
            ch.pipeline().addLast("outHandler", new OutBoundHandler());
            //心跳，连接空闲时间太长时，将会触发一个IdleStateEvent 事件，在 userEventTriggered 方法中处理
            ch.pipeline().addLast("idleStateHandler",
                new IdleStateHandler(READER_IDLE_TIME_SECONDS, WRITER_IDLE_TIME_SECONDS, ALL_IDLE_TIME_SECONDS));
            // 报文接收
            ch.pipeline().addLast(new MessageReceiverHandler());
            // 连接管理，放到最后。
            ch.pipeline().addLast("inHandler", new LinkManagementHandler());
        }
    }


    class ServerStarterListener implements GenericFutureListener {

        @Override
        public void operationComplete(Future future) throws Exception {
            if (future.isSuccess()) {
                LOGGER.info("================>netty server 监听启动成功, 监听端口 : {}", port);
            } else {
                LOGGER.info("==============>netty server 监听启动失败, 监听端口:{}, 失败原因 : {}", port, future.cause());
            }
        }
    }
}
